﻿/*  
 *  IIS LogFile Analyser, Copyright (C) 2013. http://www.iis-analyser.com
 *  --
 *  This program is free software, provided "AS IS" WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
 * 
 *  File Author: Ian Quigley,    2013-02-23
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using LogParser.DTO;
using LogParser.Parsers;

namespace LogParser
{
    public class LogFileParser : IDisposable
    {
        private StringParser _stringParser;
        private List<IParser> _lineParsers = new List<IParser>();
        private StreamReader _reader = null;

        public DocumentHeader DocumentHeader { get; private set; }

        public LogFileParser(string filePath)
        {
            DocumentHeader = new DocumentHeader();
            DocumentHeader.FilePath = filePath;

            _stringParser = new StringParser();

            _lineParsers.Add( new CommentParser() );
            _lineParsers.Add( new CommentSoftwareParser( DocumentHeader ) );
            _lineParsers.Add( new CommentFieldParser( DocumentHeader ) );
            _lineParsers.Add( new CommentDateParser( DocumentHeader ) );
            _lineParsers.Add( new LineParser( DocumentHeader ) );

            // IIS probably has the file locked.
            // Must use FileShare.ReadWrite to allow IIS process to write while we read.
            _reader = new StreamReader( new FileStream( filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) );
        }

        public void Dispose()
        {
            if (_reader != null)
                _reader.Dispose();
        }

        public IDocumentLine Read()
        {
            while (_stringParser.IsEmpty && !_reader.EndOfStream)
                _stringParser.Feed( _reader.ReadLine() );

            if (_stringParser.IsEmpty)
                return new ReadError( "Read past end of stream." );

            IParser current = null;
            foreach (var parser in _lineParsers)
            {
                if (parser.SwitchTo( _stringParser ) && (current == null || current.Priority > parser.Priority))
                    current = parser;
            }

            if (current == null)
                return new ReadError( "Could not parse line" );

            return current.Read( _stringParser );            
        }

        public bool IsEmpty 
        { 
            get
            {
                return _stringParser.IsEmpty && _reader.EndOfStream;
            }
        }

        public bool SkipLines( long lineCount )
        {
            while (!_reader.EndOfStream && lineCount > 0)
            {
                _reader.ReadLine();
                lineCount--;
            }
            return lineCount == 0;
        }        
    }

   
}
